


var http = require('http');
var express = require('express');
var bodyParser=require("body-parser");
var multer = require('multer');
const path = require('path');
const fs = require('fs');
var outFunctions=require('./outFunctions.js');
var myDiskStorage=require('./myDiskStorage.js');



var server_config=require('../config.json' );

var workerMutiThread;
//console.log(server_config);

var port = 8888;

if(server_config && server_config.node_port){
	port = server_config.node_port;
}


//用户信息，从接口获取
var user_cache_arr =[];

//用户识别次数
var user_recognize_times_arr =[];





var app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.json({limit:"100mb"}));

var server = app.listen(port, function () {
	var host = server.address().address;
	var port = server.address().port;
	console.log("http listen at %s:%s", host, port);
});




  
app.all("*",function(req,res,next){
 //设置允许跨域的域名，*代表允许任意域名跨域
	res.header("Access-Control-Allow-Credentials", "true");
	res.header("Content-Type", "application/json; charset=utf8");
 res.header("Access-Control-Allow-Origin","*");
 //允许的header类型
 res.header("Access-Control-Allow-Headers","Content-Type,XFILENAME,XFILECATEGORY,XFILESIZE,x-requested-with,Authorization");
 //跨域允许的请求方式
 res.header("Access-Control-Allow-Methods","DELETE,PUT,POST,GET,OPTIONS");
 if (req.method.toLowerCase() == 'options'){
 res.sendStatus(200); 
//让options尝试请求快速结束
 }else{
	next(); 
 }
 
});


function getRandomInt(min, max) {
	min = Math.ceil(min);
	max = Math.floor(max);
	return Math.floor(Math.random() * (max - min + 1)) + min;
}
function getFileExtension(filename) {
	return filename.split('.').pop();
}


//
// const storage = multer.memoryStorage()
// const upload = multer({ storage: storage })



// 自定义 multer 的 diskStorage 的存储目录与文件名
var storage = myDiskStorage({
  destination: function (req, file, cb) {
  	//下面传了dir，这里就不用传了
	var uploadPath = null;//path.resolve(__dirname, '../temp_files')
    cb(null, uploadPath) // 注意一下这里，这个是设置上传到哪个文件夹的哦~~
  },
  filename: function (req, file, cb) {
	  var _GET = req.query;
	  var _POST = req.body;
	  var s = _GET['s']?_GET['s']:'';
	  // console.log(_GET);
	  // console.log(_POST);

	  var s = _GET['s']?_GET['s']:'';
	  if(s==''){
		  cb('s参数不能为空');
		  return;
	  }

	  var isLastFile = _POST['isLastFile'];
	  //console.log(req.files);
	  //console.log(req.body);

	  var jsonfileInfo = _POST.jsonfileInfo;
	  var file_info = JSON.parse(jsonfileInfo);
	  var md5 = file_info['md5'];
	  var size = file_info['size'];
	  var inner_dir;
	  if(md5){
		  inner_dir = md5+'_'+size;
	  }else{
		  inner_dir = getMd5ByStr(file_info['name'])+'_'+size;
	  }
	  var each_cut_len = file_info['each_cut_len'];


	  var file_index = _POST['index'];

	  //获取当前文件所在的绝对目录
	  var dir =  '..'+"/temp_files"+'/'+s+'/'+inner_dir;
	  //files
	  // mkdirs(dir);

	  var file_path = dir+"/" + file_index;

	  var fileName = 'temp_'+file_info['name'];

	  // var outputFile = dir+'/'+'temp_'+file_info['name'];
	  var position = file_index * each_cut_len;

	  // await fileCoverValAtPos(outputFile,contentToWrite,position);

	  var outputFile = dir+'/'+fileName;
	  // console.log(outputFile,position);
	  fileCoverValAtPosBefore(outputFile,position);

	  var createWriteStreamOptions = {
		  flags: 'r+', // 默认是'w'，此处改为'a'以追加模式写入 字符串，默认为'w'。文件的打开行为。'w'为写入模式，'a'为追加模式，'r+'为读写模式等。
		  encoding: null, // 设置文件的编码为utf8 字符串，默认为'null'。文件的编码。当指定为'null'时，数据将作为Buffer对象写入。
		  fd: null, // 文件描述符，如果不使用默认为null 数字，默认为null。通过传递文件描述符来指定要写入的文件。
		  mode: 0o666, //0o666 设置文件模式（权限），在创建文件时生效 整数，用于设置文件模式（权限）。
		  start: position, // 开始写入文件的位置，默认为0 整数，用于指定从文件的哪个位置开始写入。
		  autoClose: true, // 写入结束后是否自动关闭文件，默认为true 布尔值，默认为true。表示是否在结束时自动关闭文件描述符。
		  highWaterMark: 5*1024 * 1024 // 流控制的缓冲级别，单位为字节 数字，控制用于内部缓冲区的字节数。
	  };
	  // console.log(createWriteStreamOptions);
	  var needMd5 = _POST['md5']!='';
    cb(null, fileName,createWriteStreamOptions,dir,file_index,needMd5);
  }
})

var upload = multer({ storage: storage })



app.post("/file_piece_upload", upload.any(),async function(req,res){
		try{
		var _GET = req.query;
		var _POST = req.body;
			
		var s = _GET['s']?_GET['s']:'';
		if(s==''){
			res.json({"msg":'s参数不能为空',code:-1,"data":[]});
			res.end();
			return;
		}


		var isLastFile = _POST['isLastFile'];
		//console.log(req.files);
		//console.log(req.body);
		var fileBuffer;
		// if(req.files && req.files.length>0 && req.files[0].buffer){
		// 	//不是最后一个文件直接返回成功，剩下的服务器端自己执行，加速上传进度
		// 	if(!isLastFile){
		// 		res.json({"msg":'ok',code:1,"data":[]});
		// 		res.end();
		// 	}
		// 	fileBuffer = req.files[0].buffer;
		// }else{
		// 	res.json({"msg":'文件为空',code:-1,"data":[]});
		// 	res.end();
		// 	return;
		// }
		var jsonfileInfo = _POST.jsonfileInfo;
		var file_info = JSON.parse(jsonfileInfo);
		var md5 = file_info['md5'];
		var size = file_info['size'];
		var inner_dir;
		if(md5){
			inner_dir = md5+'_'+size;
		}else{
			inner_dir = getMd5ByStr(file_info['name'])+'_'+size;
		}
		var each_cut_len = file_info['each_cut_len'];
		
		

		var file_index = _POST['index'];
		
		//获取当前文件所在的绝对目录
		var dir =  '..'+"/temp_files"+'/'+s+'/'+inner_dir;
		//files
		// mkdirs(dir);
		
		var file_path = dir+"/" + file_index;
		
		if(_POST['md5']!=''){
			var md5_upload_file="";
			console.log(req.files);
			if(req.files && req.files.length>0 && req.files[0].md5){
				md5_upload_file = req.files[0].md5;
			}
			// var md5_upload_file = getMd5ByArrayBuffer(fileBuffer);
			if(md5_upload_file!=_POST['md5']){
				console.log({"msg":'文件md5校验失败',code:-1,"data":{
					'md5_upload_file':md5_upload_file,
					'md5_post':_POST['md5'],
				}});
				return;
			}
		}
		
		
		var outputFile = dir+'/'+'temp_'+file_info['name'];
		var contentToWrite = fileBuffer;
		var position = file_index * each_cut_len;
		// await fileCoverValAtPos(outputFile,contentToWrite,position);

		//然后在原来的数字id文件写一个md5，没有md5就写一个时间戳
		var tempFileValue = md5;
		if(!tempFileValue){
			tempFileValue = time()+"";
		}
		//console.log(file_path);
		//console.log(tempFileValue);
		file_put_contents(file_path,tempFileValue);
		
	//console.log("到这里了1");
	//不是最后一个文件不需要执行后面的检查了
	if(!isLastFile){
		res.json({"msg":'ok',code:1,"data":[]});
		res.end();	
		return;
	}
		
	//计算文件个数
	var file_num = Math.ceil(size/each_cut_len);
	
	var file_name_key_check_arr = [];
	for(var i=0;i<file_num;i++){
		file_name_key_check_arr.push(0);
	}
	
	var temp_file_arr = [];
	var files = scandir(dir);  // 获取文件夹下所有文件和文件夹
	//console.log(files);
	for(var i=0;i<files.length;i++) {
		var file = files[i];
		//console.log(file,is_numeric(file));
		if (is_numeric(file)) {  // 排除当前目录和上层目录
			temp_file_arr.push(file);
			if(file_name_key_check_arr[file]!== undefined){
				file_name_key_check_arr[file]=1;
			}
		}
	}
	
	var exist_file_name_arr = [];
	var not_exist_file_name_arr = [];
	for(var i=0;i<file_name_key_check_arr.length;i++) {
		var file = i;
		var exist = file_name_key_check_arr[i];
		if(exist){
			exist_file_name_arr.push(file);
		}else{
			not_exist_file_name_arr.push(file);
		}
	}
	
	//console.log("到这里了2");
	//console.log(not_exist_file_name_arr);
	//所有文件都上传完毕，则把文件移动到对应目录
	if(not_exist_file_name_arr.length==0){
	//console.log("到这里了3");
		//这里还需要读取计算最终文件的md5，还没有做

		outputFile = dir+'/'+'temp_'+file_info['name'];
		var targetDir = ".."+"/files"+'/'+s;
		mkdirs(targetDir);
		var targetFile = targetDir+'/'+file_info['name'];
		if (rename(outputFile, targetFile)) {
			//echo "文件移动成功".targetFile;
		} else {
				console.log({"msg":'文件移动失败',code:-1,"data":{
					'outputFile':outputFile,
					'targetFile':targetFile,
				}});
				return;
		}
		try {
			deleteDirectory(dir);
		} catch (e) {
			console.log(e);
		}
	}
	
	//console.log("到这里了4");
	res.json({"msg":'ok',code:1,"data":[]});
	res.end();	
	
		
		}catch (e) {
			console.log(e);
			res.json({"msg":e.message,code:0});
			res.end();	
		}
	
});



//测试接口
app.get("/test",async function(req,res){
		
		res.json({"msg":'ok',code:1});
		res.end();	
	
});



	function getMd5ByStr(str){
		if(typeof SparkMD5=="undefined"){
			SparkMD5 = require('./spark-md5.min.js')
		}
		var spark = new SparkMD5();
		spark.append(str);
		var md5 = spark.end();
		return md5;
	}


	function getMd5ByArrayBuffer(arrayBuffer){
		if(typeof SparkMD5=="undefined"){
			SparkMD5 = require('./spark-md5.min.js')
		}
		var spark = new SparkMD5.ArrayBuffer();
		spark.append(arrayBuffer);
		var md5 = spark.end();
		return md5;
	}
 
function mkdirs(dirPath) {
  const absolutePath = path.resolve(dirPath);
 
  // 若路径已存在，直接返回
  if (fs.existsSync(absolutePath)) {
    return;
  }
 
  // 去除最后一个斜杠
  const parentPath = path.dirname(absolutePath);
  
  // 递归创建父级目录
  mkdirs(parentPath);
 
  fs.mkdirSync(absolutePath);
}
 
function file_put_contents(dirPath,content) {
 
  fs.writeFileSync(dirPath, content);
  //console.log('文件写入成功！');
 
}

function fileCoverValAtPosBefore(filePath,position){

	var fileSize = 0;
	//文件不存在则创建文件
	if(!fs.existsSync(filePath)){
		//创建一个空文件
		file_put_contents(filePath,'');
	}else{
		//文件存在则获取文件大小
		var stats = fs.statSync(filePath);
		var fileSize = stats.size; // 文件大小（以字节为单位）
	}

	var each_len =  10 * 1024 * 1024;//分段写入的字节数，暂时设定为3MB，防止内存占用过多
	//需要增加的文件大小
	var fileSizeAdd = position - fileSize;
	var fileSizeRemain = fileSizeAdd;
	//如果文件大小小于写入位置，则填充0到写入位置之前
	if(fileSizeRemain>0){
		// 打开文件，获取文件句柄
		var content = '';
		while(fileSizeRemain>0){
			var writeLen = each_len;
			if(fileSizeRemain<each_len){
				writeLen = fileSizeRemain;
			}
			// 如果字符串长度不等于要写入的长度，则生成字符串
			if(content.length!=writeLen){
				content = new Array(each_len).join('0'); // 这里使用"0"填充文件，可以根据需要修改
			}
			fs.appendFileSync(filePath, content);
			fileSizeRemain = fileSizeRemain - writeLen;
		}

	}
}


	async function fileCoverValAtPos(filePath,contentToWrite,position){

		fileCoverValAtPosBefore(filePath,position);
		/*
			var threads_parameter_arr = [];
			threads_parameter_arr.push({
				filePath:filePath,
				contentToWrite:contentToWrite,
				position:position,
			});
				
			var projectAbsolutePath=__dirname+'/outFunctions.js';
			var functionName="fileCoverValAtPosInner";
			if(!workerMutiThread){
				workerMutiThread = require('./workerMutiThread.js');
			}
			var mutiThreadsRes = await workerMutiThread.mutiThreadsDealing(projectAbsolutePath,functionName,threads_parameter_arr);
		return mutiThreadsRes;
		*/
		
		return outFunctions.fileCoverValAtPosInner(filePath,contentToWrite,position);
		
	}


function time(){
	return Math.floor(new Date().getTime()/1000);
}

function scandir(directoryPath){
	var  files = fs.readdirSync(directoryPath);
	//console.log(files);
	//var file_arr = [];
    //files.forEach(file => {
        //console.log(file); // 这里将打印出每个文件名
	//	file_arr.push(file);
    //});
	//console.log(file_arr);
	return files;
}
function is_numeric(str) {
  return Number.isFinite(+str);
}

function rename(sourcePath, destPath) {
  fs.renameSync(sourcePath, destPath);
  return 1;
}

function deleteDirectory(dirPath) {
  // 判断路径是否存在
  if (fs.existsSync(dirPath)) {
    const files = fs.readdirSync(dirPath);
    files.forEach(function(file) {
      const curPath = dirPath + '/' + file;
      // 判断是否是目录，是则递归删除子目录
      if (fs.statSync(curPath).isDirectory()) {
        deleteDirectory(curPath);
      } else {
        // 是文件则直接删除
        fs.unlinkSync(curPath);
      }
    });
    // 删除空目录
    fs.rmdirSync(dirPath);
  }
}